package stendcontrol;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Properties;

public class Configurator implements Serializable {
    //глобальные константы

    public static final int SHORT_LEN_MSG = 6;
    public static final int LONG_LEN_MSG = 11;
    public static final int MAX_SIZE_MEASURE_SEQUENCE = 1000;
    public static final int COUNT_SENSOR = 3;
    public static final int COUNT_MOTORS = 4;
    public static final int ADC_CAPACITY = 12;
    //общее количество параметров системы
    private static int count_props = 11;
    //константы параметров
    public static final int UDP_OUT_PORT = 0; //UDP_PORT Out:0xC939(51513)
    public static final int UDP_IN_PORT = 1; //UDP_PORT In:0xC93A(51514)
    public static final int UDP_OUT_IP = 2; //UDP_PORT In:0xC93A(51514)
    public static final int DB_SERVER = 3;
    public static final int DB_PORT = 4;
    public static final int DB_USER = 5;
    public static final int DB_PASSWORD = 6;
    public static final int DB_NAME = 7;
    public static final int RESEARCH_PATH = 8;
    public static final int RESEARCH_EXT_FILES = 9;
    public static final int FS_SAVE_PATH = 10;
    //имена параметров в файле конфигурации
    private static final ConfParameter[] params = {
        new ConfParameter(UDP_OUT_PORT, "udp_out_port", "51513", ConfParameter.T_INTEGER,ConfParameter.I_INTEGER,"Входной порт"),
        new ConfParameter(UDP_IN_PORT, "udp_in_port", "51514", ConfParameter.T_INTEGER,ConfParameter.I_INTEGER,"Выходной порт"),
        new ConfParameter(UDP_OUT_IP, "udp_out_ip", "192.168.0.255", ConfParameter.T_STRING,ConfParameter.I_STRING,"Широковешательный IP сети"),
        new ConfParameter(DB_SERVER, "db_server", "localhost", ConfParameter.T_STRING,ConfParameter.I_STRING,"Сервер базы данных"),
        new ConfParameter(DB_PORT, "db_port", "3306", ConfParameter.T_INTEGER,ConfParameter.I_INTEGER,"Порт сервера базы данных"),
        new ConfParameter(DB_USER, "db_user", "root", ConfParameter.T_STRING,ConfParameter.I_STRING,"Имя пользователя базы данных"),
        new ConfParameter(DB_PASSWORD, "db_password", "", ConfParameter.T_STRING,ConfParameter.I_STRING,"Пароль базы данных"),
        new ConfParameter(DB_NAME, "db_name", "test", ConfParameter.T_STRING,ConfParameter.I_STRING,"Имя базы данных"),
        new ConfParameter(RESEARCH_PATH, "research_path", "." + File.separator + "researches" + File.separator, ConfParameter.T_STRING,ConfParameter.I_DIR,"Директория исследований"),
        new ConfParameter(RESEARCH_EXT_FILES, "research_ext_files", ".rsch", ConfParameter.T_STRING,ConfParameter.I_STRING,"Расширение скриптов исследований"),
        new ConfParameter(FS_SAVE_PATH, "fs_save_path","." + File.separator + "experiments" + File.separator, ConfParameter.T_STRING,ConfParameter.I_DIR,"Директория экспериментов")
    };
    //файл настроек системы
    private final String filename_config = "config.ini";
    Properties props = new Properties();
    private static Configurator conf = null;

    public Configurator() {
        if (count_props != params.length) {
            //Поправить соответствие массива params количеству констант 
            //и count_props
            throw new ArrayIndexOutOfBoundsException("Params contain bad"
                    + "count properties");
        }
        loadProps();
    }

    private File getFileConfig() {
        File fl_props = new File(filename_config);
        if (fl_props.exists()) {
            return fl_props;
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "File config \""
                    + fl_props.getAbsolutePath()
                    + "\" not found");
            StendLogger.log(StendLogger.MSG_WARNING, "File \""
                    + fl_props.getAbsolutePath()
                    + "\" will be created with default config");
            return createDefaultFileProps();
        }
    }

    /**
     * Загрузка параметров из файла в props
     */
    private void loadProps() {
        File fl_props = getFileConfig();
        if (fl_props != null) {
            if (fl_props.canRead()) {
                try {
                    props.load(new FileInputStream(fl_props));
                } catch (IOException ex) {
                    StendLogger.log(StendLogger.MSG_ERROR, "Error in read"
                            + " config file \""
                            + fl_props.getAbsolutePath() + "\"");
                    loadDefaultProps();
                    StendLogger.log(StendLogger.MSG_WARNING,
                            "Loaded defaul parameters");
                }
                for (int i = 0; i < count_props; i++) {
                    if (!props.containsKey(params[i].getName())) {
                        loadDefaultProp(i);
                        StendLogger.log(StendLogger.MSG_WARNING, "Property \""
                                + params[i].getName() + "\" is not contain in"
                                + "config file: \"" + fl_props.getAbsolutePath()
                                + "\". Loaded default value");
                    }
                }
            } else {
                StendLogger.log(StendLogger.MSG_ERROR, "Can`t read"
                        + " config file \""
                        + fl_props.getAbsolutePath() + "\"");
                loadDefaultProps();
                StendLogger.log(StendLogger.MSG_WARNING,
                        "Loaded defaul parameters");
            }
        }

    }

    private File createDefaultFileProps() {
        File fl_props = new File(filename_config);
        try {
            if (fl_props.createNewFile()) {
                FileOutputStream fos = new FileOutputStream(fl_props);
                String row_conf;
                for (int i = 0; i < count_props; i++) {
                    row_conf = params[i].getName() + "="
                            + params[i].getDefault() + "\n";
                    fos.write(row_conf.getBytes());
                }
                fos.close();
            }

        } catch (IOException ex) {
            StendLogger.log(StendLogger.MSG_ERROR, "File \""
                    + filename_config + "\" not created. Not access");
            fl_props = null;
        }

        loadDefaultProps();
        StendLogger.log(StendLogger.MSG_WARNING, "Loaded defaul parameters");
        return fl_props;
    }

    private void loadDefaultProps() {
        for (int i = 0; i < count_props; i++) {
            loadDefaultProp(i);
        }
    }

    private void loadDefaultProp(int n) {
        props.setProperty(params[n].getName(), params[n].getDefault());
    }
    //хрранит возвращаемый объект getProperty
    Object bobj = null;

    /**
     * Operation
     *
     * @param name
     * @return Object
     */
    public Object getProperty(int prop) {

        try {
            switch (params[prop].getType()) {
                case ConfParameter.T_INTEGER: {
                    bobj = new Integer(props.getProperty(params[prop].getName()));
                    break;
                }
                case ConfParameter.T_LONG: {
                    bobj = new Long(props.getProperty(params[prop].getName()));
                    break;
                }
                case ConfParameter.T_DOUBLE: {
                    bobj = new Double(props.getProperty(params[prop].getName()));
                    break;
                }
                case ConfParameter.T_STRING: {
                    bobj = props.getProperty(params[prop].getName());
                    break;
                }
            }
        } catch (Exception exp) {
            StendLogger.log(StendLogger.MSG_ERROR, "Error in return property"
                    + " from config. propName: " + params[prop].getName()
                    + " value: " + props.getProperty(params[prop].getName(),
                    params[prop].getDefault()) + " Exception message: "
                    + exp.getMessage());
            switch (params[prop].getType()) {
                case ConfParameter.T_INTEGER: {
                    bobj = new Integer(params[prop].getDefault());
                    break;
                }
                case ConfParameter.T_LONG: {
                    bobj = new Long(params[prop].getDefault());
                    break;
                }
                case ConfParameter.T_DOUBLE: {
                    bobj = new Double(params[prop].getDefault());
                    break;
                }
                case ConfParameter.T_STRING: {
                    bobj = props.getProperty(params[prop].getDefault());
                    break;
                }
            }

        }
        return bobj;
    }

    public void setProperty(int prop, Object value) {
        props.setProperty(params[prop].getName(), value.toString());
    }

    public void addProperty(String name_prop, String value) {
        props.setProperty(name_prop, value);
    }

    public String getProperty(String name_prop) {
        return props.getProperty(name_prop);
    }

    public void setProperty(String name_prop, String value) {
        props.setProperty(name_prop, value);
    }

    public void saveConfig() {
        File fl_props = getFileConfig();
        if (fl_props != null) {
            if (fl_props.canWrite()) {
                try {
                    FileOutputStream fos = new FileOutputStream(fl_props);
                    props.store(fos,
                            "genered in StendControl");
                    fos.close();
                } catch (IOException ex) {
                    StendLogger.log(StendLogger.MSG_ERROR, "Error in writting"
                            + " config file: " + fl_props.getAbsolutePath());
                }
            }
        } else {
            StendLogger.log(StendLogger.MSG_ERROR, "Problem with save config.");
        }
    }

    public static ConfParameter[] getParams() {
        return params;
    }

    public static synchronized Configurator getConf() {
        if (conf == null) {
            conf = new Configurator();
        }
        return conf;
    }
}
